/*  SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************

  Audacity: A Digital Audio Editor

  CloudProjectsDatabase.h

  Dmitry Vedenko

**********************************************************************/
#pragma once

#include <mutex>
#include <optional>
#include <string>
#include <vector>

#include "sqlite/SafeConnection.h"

#include "CloudSyncDTO.h"

namespace audacity::cloud::audiocom::sync {
struct DBProjectData final
{
    std::string ProjectId;
    std::string SnapshotId;
    int64_t SavesCount       = 0;
    int64_t LastAudioPreview = 0;
    std::string LocalPath;
    int64_t LastModified = 0;
    int64_t LastRead     = 0;

    bool FirstSyncDialogShown { false };

    enum SyncStatusType
    {
        SyncStatusSynced      = 0,
        SyncStatusUploading   = 1,
        SyncStatusDownloading = 2,
    } SyncStatus = {};
};

struct PendingSnapshotData final
{
    std::string ProjectId;
    std::string SnapshotId;
    std::string ConfirmUrl;
};

struct PendingProjectBlobData final
{
    std::string ProjectId;
    std::string SnapshotId;

    std::string UploadUrl;
    std::string ConfirmUrl;
    std::string FailUrl;

    std::vector<uint8_t> BlobData;
};

struct PendingProjectBlockData final
{
    std::string ProjectId;
    std::string SnapshotId;

    std::string UploadUrl;
    std::string ConfirmUrl;
    std::string FailUrl;

    int64_t BlockId {};
    int BlockSampleFormat {};

    std::string BlockHash;
};

class CloudProjectsDatabase final
{
    CloudProjectsDatabase() = default;
    ~CloudProjectsDatabase() = default;

public:
    static CloudProjectsDatabase& Get();

    sqlite::SafeConnection::Lock GetConnection();
    const sqlite::SafeConnection::Lock GetConnection() const;

    std::optional<DBProjectData>
    GetProjectData(std::string_view projectId) const;
    std::optional<DBProjectData>
    GetProjectDataForPath(const std::string& projectPath) const;

    std::vector<DBProjectData> GetCloudProjects() const;

    void DeleteProject(std::string_view projectId);

    bool
    MarkProjectAsSynced(std::string_view projectId, std::string_view snapshotId);

    void UpdateProjectBlockList(
        std::string_view projectId, const SampleBlockIDSet& blockSet);

    std::optional<std::string>
    GetBlockHash(std::string_view projectId, int64_t blockId) const;

    void UpdateBlockHashes(
        std::string_view projectId, const std::vector<std::pair<int64_t, std::string>>& hashes);

    bool UpdateProjectData(const DBProjectData& projectData);

    bool IsFirstSyncDialogShown(std::string_view projectId) const;
    void SetFirstSyncDialogShown(std::string_view projectId, bool shown = true);

    std::string GetProjectUserSlug(std::string_view projectId);
    void SetProjectUserSlug(std::string_view projectId, std::string_view slug);

    bool IsProjectBlockLocked(std::string_view projectId, int64_t blockId) const;

    void AddPendingSnapshot(const PendingSnapshotData& snapshotData);
    void RemovePendingSnapshot(
        std::string_view projectId, std::string_view snapshotId);
    std::vector<PendingSnapshotData>
    GetPendingSnapshots(std::string_view projectId) const;

    void AddPendingProjectBlob(const PendingProjectBlobData& blobData);
    void RemovePendingProjectBlob(
        std::string_view projectId, std::string_view snapshotId);
    std::optional<PendingProjectBlobData> GetPendingProjectBlob(
        std::string_view projectId, std::string_view snapshotId) const;

    void AddPendingProjectBlocks(
        const std::vector<PendingProjectBlockData>& blockData);
    void RemovePendingProjectBlock(
        std::string_view projectId, int64_t blockId);
    void RemovePendingProjectBlocks(
        std::string_view projectId, std::string_view snapshotId);
    std::vector<PendingProjectBlockData> GetPendingProjectBlocks(
        std::string_view projectId, std::string_view snapshotId);

private:
    std::optional<DBProjectData>
    DoGetProjectData(const sqlite::Row& result) const;

    std::optional<DBProjectData>
    DoGetProjectData(sqlite::RunResult result) const;

    bool OpenConnection();
    bool RunMigrations();

    bool DeleteProject(sqlite::SafeConnection::Lock& connection, std::string_view projectId);

    std::mutex mConnectionMutex;
    std::shared_ptr<sqlite::SafeConnection> mConnection;
};
} // namespace audacity::cloud::audiocom::sync
